﻿using Common;
using MuseLibrary;
using SignalProcessing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms.DataVisualization.Charting;

namespace MuseUI.ObservableData
{
    public class EegObservableData : IObservableData
    {
        public EegObservableData()
        {
            _eegData = new ScrollingList<int>[4];
            for (int i = 0; i < 4; i++)
            {
                _eegData[i] = new ScrollingList<int>(POINT_COUNT, 0);
            }

            _fourierPredominantFrequency = new ScrollingList<double>(POINT_COUNT,0 );

            MakeSeries("EEG CH1", Colours.BlueForWhiteBackground, SeriesChartType.FastLine, "Scroll", true, true);
            MakeSeries("EEG CH2", Colours.GreenForWhiteBackground, SeriesChartType.FastLine, "Scroll", true, true);
            MakeSeries("EEG CH3", Colours.RedForWhiteBackground, SeriesChartType.FastLine, "Scroll", true, true);
            MakeSeries("EEG CH4", Colours.PurpleForWhiteBackground, SeriesChartType.FastLine, "Scroll", true, true);
            MakeSeries("Fourier", Colours.BlueForWhiteBackground, SeriesChartType.FastLine, "Fourier", true, false);
            //MakeSeries("Fourier CH2", Colours.GreenForWhiteBackground, SeriesChartType.FastLine, "Fourier", false);
            //MakeSeries("Fourier CH3", Colours.RedForWhiteBackground, SeriesChartType.FastLine, "Fourier", false);
            //MakeSeries("Fourier CH4", Colours.PurpleForWhiteBackground, SeriesChartType.FastLine, "Fourier", false);
            MakeSeries("Fourier scroll", Colours.BlueForWhiteBackground, SeriesChartType.FastLine, "FourierScroll", true, false);
            //MakeSeries("Fourier scroll CH2", Colours.BlueForWhiteBackground, SeriesChartType.FastLine, "FourierScroll", false);
            //MakeSeries("Fourier scroll CH3", Colours.BlueForWhiteBackground, SeriesChartType.FastLine, "FourierScroll", false);
            //MakeSeries("Fourier scroll CH4", Colours.BlueForWhiteBackground, SeriesChartType.FastLine, "FourierScroll", false);

            _chartAreas.Add(ChartingHelper.InitialiseChartArea("Scroll", new Double[] { 0, 10, 0, 1024 }, new float[] { 1, 1, 50, 98 }, true));
            _chartAreas.Add(ChartingHelper.InitialiseChartArea("Fourier", new Double[] { 0, 110, 0, 1 }, new float[] { 52, 1, 46, 48 }, false));
            _chartAreas.Add(ChartingHelper.InitialiseChartArea("FourierScroll", new Double[] { 0, 10, 0, 110 }, new float[] { 52, 50, 48, 48 }, true));
        }

        private void MakeSeries(string seriesName, Color seriesColour, SeriesChartType seriesChartType, string chartAreaName, bool isShown, bool isRecording)
        {
            _allSeries.Add(new Series(seriesName) { Color = seriesColour, ChartType = seriesChartType, ChartArea = chartAreaName });
            var option = new ObservableDataOption(seriesName, isShown, seriesColour, isRecording);
            _chartingOptions.Add(option);
        }

        private int _channelForFourier = 0;
        public int ChannelForFourier
        {
            get { return _channelForFourier; }
            set
            {
                lock (_lockFourierChannelChange) 
                    _channelForFourier = value;
            }
        }

        private bool _isRecording = false;
        public bool IsRecording
        {
            get { return _isRecording; }
            set { _isRecording = value; }
        }
        
        private object _lockEegData = new object();
        public void InsertData(DateTime date, int[] data)
        {
            if (IsRecording)
                _recordedData.Add(date, new List<int>());

            lock (_eegData)
            {
                for (int i = 0; i < 4; i++)
                {
                    _eegData[i].AddItem(data[i]);
                    if (IsRecording && _chartingOptions[i].IsRecording)
                        _recordedData[date].Add(data[i]);
                }
            }
        }

        private Dictionary<DateTime, List<int>> _recordedData = new Dictionary<DateTime, List<int>>();

        public void OutputRecordedData(String filePath)
        {
            var fileBuilder = new StringBuilder();
            fileBuilder.Append("Date");

            var recordedChannels = new List<int>();
            for (int i = 0; i < 4; i++)
            {
                if (_chartingOptions[i].IsRecording)
                    fileBuilder.AppendFormat(",CH{0}", i + 1);
            }
            
            fileBuilder.AppendLine();
            foreach (var dateVal in _recordedData)
            {
                fileBuilder.Append(dateVal.Key.ToString("yyyy-MM-dd HH:mm:ss.fff"));
                for (int i = 0; i < dateVal.Value.Count; i++)
                {
                    fileBuilder.AppendFormat(",{0}", dateVal.Value[i]);
                }

                fileBuilder.AppendLine();
            }

            using (var file = new StreamWriter(filePath))
            {
                file.Write(fileBuilder);
            }

            _recordedData.Clear();
        }

        public List<Series> UpdateSeries()
        {
            var clonedSeries = new List<Series>();
            for (int i = 0; i < SERIES_COUNT; i++)
            {
                clonedSeries.Add(_allSeries[i].Clone());
            }

            for (int pointIndex = 0; pointIndex < POINT_COUNT; pointIndex++)
            {
                var time = 10.0 - ((Double)pointIndex * TIME_DELTA);
                for (int seriesIndex = 0; seriesIndex < 4; seriesIndex++)
                {
                    if (ChartingOptions[seriesIndex].IsShown)
                        clonedSeries[seriesIndex].Points.AddXY(time, _eegData[seriesIndex][pointIndex]);
                }
            }


            UpdateFourier(clonedSeries[4], clonedSeries[5]);

            return clonedSeries;
        }

        private object _lockFourierPersist = new object();
        private object _lockFourierChannelChange = new object();
        private Fourier _fourierProcessor = new Fourier();
        private void UpdateFourier(Series fourierSeries, Series fourierScrollingSeries)
        {
            int channelForFourier;
            lock (_lockFourierChannelChange)
                channelForFourier = _channelForFourier;
            var array = new Double[256];
            for (int i = 0; i < 256; i++)
                array[i] = _eegData[channelForFourier][POINT_COUNT - i - 1];
            Double predominantFrequency, maxAmplitude;
            var fourier = _fourierProcessor.FourierTransformAveraged(array, TIME_DELTA, 10, out predominantFrequency, out maxAmplitude);

            if (fourier == null)
                return;

            if (maxAmplitude == 0)
                maxAmplitude = 1;

            lock (_lockEegData)
                _fourierPredominantFrequency.AddItem(predominantFrequency);

            for (int pointIndex = 0; pointIndex < POINT_COUNT; pointIndex++)
            {
                fourierScrollingSeries.Points.AddXY(10.0 - ((Double)pointIndex * TIME_DELTA), _fourierPredominantFrequency[pointIndex]);
            }

            foreach (var frequencyAmplitude in fourier)
            {
                fourierSeries.Points.AddXY(frequencyAmplitude.Key, frequencyAmplitude.Value / maxAmplitude);
            }
        }

        private List<Series> _allSeries = new List<Series>();
        public List<Series> AllSeries
        {
            get { return _allSeries; }
        }

        private List<ChartArea> _chartAreas = new List<ChartArea>();
        public List<ChartArea> ChartAreas
        {
            get { return _chartAreas; }
        }

        private bool _isVisible = false;
        public bool IsVisible
        {
            get { return _isVisible; }
            set { _isVisible = value; }
        }

        private BindingList<ObservableDataOption> _chartingOptions = new BindingList<ObservableDataOption>();
        public BindingList<ObservableDataOption> ChartingOptions
        {
            get { return _chartingOptions; }
        }

        public MuseChartType ChartType { get { return MuseChartType.Eeg; } }

        private ScrollingList<int>[] _eegData;
        private ScrollingList<Double> _fourierPredominantFrequency;
        private Double _currentTimeOffset;
        private const Double TIME_DELTA = 1.0 / 220.0;
        private const int SERIES_COUNT = 6;
        private const int POINT_COUNT = 220 * 10;


    }
}
